package controllers

import java.io.File

import actor.MissionManageActor
import akka.actor.{Actor, ActorSystem, PoisonPill, Props}
import akka.stream.Materializer
import command.{CommandUtils, MissionUtils}
import dao._
import javax.inject.{Inject, Singleton}
import models.Tables._
import org.apache.commons.io.FileUtils
import org.joda.time.DateTime
import org.zeroturnaround.zip.ZipUtil
import play.api.libs.json.{JsObject, JsValue, Json}
import play.api.libs.streams.ActorFlow
import play.api.mvc.{AbstractController, ControllerComponents, WebSocket}
import tool.{FormTool, SampleTool, Tool, WebTool}
import utils.Utils

import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
import scala.concurrent.duration._
import implicits.Implicits._
import shared.VarTool

import scala.collection.SeqMap

/**
 * Created by Administrator on 2019/8/8
 */
@Singleton
class SampleController @Inject()(cc: ControllerComponents, formTool: FormTool
                                )(implicit val system: ActorSystem,
                                  implicit val modeDao: ModeDao,
                                  implicit val missionDao: MissionDao,
                                  implicit val userDao: UserDao,
                                  implicit val sampleDao: SampleDao,
                                  implicit val userColumnDao: UserColumnDao,
                                  implicit val accountDao: AccountDao,
                                  implicit val materializer: Materializer
                                ) extends
  AbstractController(cc) {

  import FormTool._

  def sampleManageBefore = Action { implicit request =>
    val data = formTool.missionIdOptionForm.bindFromRequest().get
    val missionName = s"reRun_${Tool.generateMissionName}"
    Ok(views.html.user.sampleManage(missionName, data.missionId))
  }

  def sampleSummaryBefore = Action { implicit request =>
    val data = FormTool.sampleSummaryForm.bindFromRequest().get
    val onlyPanel = FormTool.onlyPanelForm.bindFromRequest().get.onlyPanel
    data.panel.toUpperCase match {
      case "YARN" => Ok(views.html.user.yarnSampleSummary(data.id, onlyPanel))
      case "SETB" => Ok(views.html.user.setBSampleSummary(data.id, onlyPanel))
      case "SETA" => Ok(views.html.user.sampleSummary(data.id, onlyPanel))
    }
  }

  def getSampleById = Action.async { implicit request =>
    val data = FormTool.sampleIdForm.bindFromRequest().get
    val userId = Tool.getUserId
    sampleDao.selectById(userId, data.id).map { x =>
      val json = Utils.getJsonByT(x)
      Ok(json)
    }
  }

  def getCurrentTime = Action { implicit request =>
    val timeStr = Tool.generateMissionName
    Ok(Json.obj("time" -> timeStr))
  }

  def getAllSample = Action.async {
    implicit request =>
      val userId = Tool.getUserId
      sampleDao.selectAll(userId).map {
        x =>
          val array = Utils.getArrayByTs(x, "siteInfo")
          Ok(Json.toJson(array))
      }
  }

  def getAllSampleIds = Action.async {
    implicit request =>
      val userId = Tool.getUserId
      sampleDao.selectAllId(userId).map {
        x =>
          val array = x.map(id => Map("id" -> id))
          Ok(Json.toJson(array))
      }
  }

  def getAllMissionName = Action.async {
    implicit request =>
      val userId = Tool.getUserId
      sampleDao.selectAllMissionName(userId).map {
        x =>
          val array = x
          Ok(Json.toJson(array))
      }
  }

  def getAllId = Action.async {
    implicit request =>
      val userId = Tool.getUserId
      sampleDao.selectAllId(userId).map {
        x =>
          val array = x
          Ok(Json.toJson(array))
      }
  }

  def getAllContainerName = Action.async {
    implicit request =>
      val userId = Tool.getUserId
      sampleDao.selectAllContainerName(userId).map {
        x =>
          val array = x
          Ok(Json.toJson(array))
      }
  }

  def getAllSampleByServer = Action.async { implicit request =>
    val userId = Tool.getUserId
    val page = FormTool.pageForm.bindFromRequest.get
    val offSet = page.offset
    val limit = page.limit
    sampleDao.selectAll(userId, page).map { case (size, x) =>
      val array = x.map { y =>
        val json = Tool.getJsonBySample(y)
        json
      }
      Ok(Json.obj("rows" -> array, "total" -> size))
    }
  }

  def getAllSampleByMissionId = Action.async {
    implicit request =>
      val userId = Tool.getUserId
      val data = FormTool.missionIdForm.bindFromRequest().get
      missionDao.selectByMissionId(data.missionId).flatMap { mission =>
        sampleDao.selectAll(userId, mission.missionName).map { x =>
          val array = x.map { y =>
            Tool.getJsonBySample(y)
          }
          Ok(Json.toJson(array))
        }
      }

  }

  def search = Action.async { implicit request =>
    val data = FormTool.sampleSearchForm.bindFromRequest().get
    val userId = Tool.getUserId
    sampleDao.selectAll(userId).map { samples =>
      val rsSamples = samples.filter { sample =>
        val idB = sample.id.like(data.id)
        val indexIdB = sample.indexId.like(data.indexId)
        val introB = sample.sampleIntro.like(data.sampleIntro)
        val sexB = if (data.gender == "all") true else sample.gender == data.gender
        val kindB = if (data.kind == "all") true else {
          VarTool.kindMap(data.kind).exists(y => sample.kind == y)
        }
        val updateMissionB = sample.updateMission.like(data.updateMission)
        val commentB = sample.comment.like(data.comment)
        val startDateB = data.startDate.map(sample.updateTime.isAfter(_)).getOrElse(true)
        val endB = data.endDate.map(y => sample.updateTime.isBefore(y)).getOrElse(true)
        idB && indexIdB && introB && sexB && kindB && commentB && updateMissionB && startDateB && endB
      }
      val json = Utils.getJsonByTs(rsSamples)
      Ok(json)
    }
  }

  def deleteSampleById = Action.async {
    implicit request =>
      val data = sampleIdForm.bindFromRequest().get
      val userId = Tool.getUserId
      sampleDao.deleteById(userId, data.id).map {
        x =>
          if (data.id.nonEmpty) {
            val sampleIdDir = Tool.getSampleIdDir(userId, data.id)
            sampleIdDir.deleteQuietly
          }
          Ok(Json.toJson("success"))
      }
  }

  def deleteSample = Action.async {
    implicit request =>
      val ids = FormTool.idsForm.bindFromRequest().get.ids
      val password = FormTool.passwordForm.bindFromRequest().get.password
      val userId = Tool.getUserId
      accountDao.selectById1.flatMap { x =>
        if (x.password == password) {
          sampleDao.deleteByIds(userId, ids).map { x =>
            ids.foreach { id =>
              if (id.nonEmpty) {
                val sampleIdDir = Tool.getSampleIdDir(userId, id)
                sampleIdDir.deleteQuietly
              }
            }
            Ok(Json.obj("valid" -> true))
          }
        } else {
          Future.successful(Ok(Json.obj("valid" -> false, "message" -> "删除失败!\n管理员密码错误，请联系管理员！")))
        }
      }
  }

  def deletes = Action.async { implicit request =>
    val data = FormTool.sampleIdsForm.bindFromRequest().get
    val userId = Tool.getUserId
    val ids = data.ids.toList
    sampleDao.deleteByIds(userId, ids).map { x =>
      Future {
        ids.foreach { id =>
          if (id.nonEmpty) {
            val sampleIdDir = Tool.getSampleIdDir(userId, id)
            sampleIdDir.deleteQuietly
          }
        }
      }
      Ok(Json.toJson("success"))
    }
  }

  def getReadsData = Action.async {
    implicit request =>
      val data = sampleIdForm.bindFromRequest().get
      val userId = Tool.getUserId
      sampleDao.selectById(userId, data.id).map {
        x =>
          val sampleIdDir = SampleTool.getSampleIdDir(x)
          val readsFile = Tool.getReadsFile(sampleIdDir)
          val json = Utils.getTxtFileJsonNoLower(readsFile)
          Ok(json)
      }
  }

  def getSampleInfo = Action.async {
    implicit request =>
      val data = sampleIdForm.bindFromRequest().get
      val userId = Tool.getUserId
      sampleDao.selectById(userId, data.id).map {
        x =>
          val json = Tool.getJsonBySample(x)
          Ok(json)
      }
  }

  def getSnpReadsData = Action.async {
    implicit request =>
      val data = sampleIdForm.bindFromRequest().get
      val userId = Tool.getUserId
      sampleDao.selectById(userId, data.id).map {
        x =>
          val sampleIdDir = SampleTool.getSampleIdDir(x)
          val readsFile = Tool.getSnpReadsFile(sampleIdDir)
          val json = Utils.getTxtFileJsonNoLower(readsFile)
          Ok(json)
      }
  }

  def getSeqData = Action.async {
    implicit request =>
      val data = sampleIdForm.bindFromRequest().get
      val userId = Tool.getUserId
      sampleDao.selectById(userId, data.id).map {
        x =>
          Ok(Json.toJson(""))
      }
  }

  def getBasicData = Action.async {
    implicit request =>
      val data = sampleIdForm.bindFromRequest().get
      val userId = Tool.getUserId
      sampleDao.selectById(userId, data.id).map {
        x =>
          val sampleIdDir = SampleTool.getSampleIdDir(x)
          val readsFile = Tool.getBasicFile(sampleIdDir)
          val json = Utils.getTxtFileJsonNoLower(readsFile)
          Ok(json)
      }
  }

  def getStatData = Action.async {
    implicit request =>
      val data = sampleIdForm.bindFromRequest().get
      val userId = Tool.getUserId
      sampleDao.selectById(userId, data.id).map {
        x =>
          val sampleIdDir = SampleTool.getSampleIdDir(x)
          val file = Tool.getStatFile(sampleIdDir)
          val json = Utils.getTxtFileJsonNoLower(file)
          Ok(json)
      }
  }

  def downloadReport = Action {
    implicit request =>
      val data = formTool.sampleIdForm.bindFromRequest().get
      val userId = Tool.getUserId
      val sampleIdDir = Tool.getSampleIdDir(userId, data.id)
      val resultFile = new File(sampleIdDir, s"${
        data.id
      }.xlsx")
      Ok.sendFile(resultFile).withHeaders(
        CONTENT_DISPOSITION -> s"${
          Tool.getContentDisposition(resultFile.getName)
        }",
        CONTENT_TYPE -> "application/x-download"
      )
  }

  def exportReports = Action {
    implicit request =>
      val data = FormTool.sampleIdsForm.bindFromRequest().get
      val tmpDir = Tool.createTempDirectory("tmpDir")
      val reportDir = new File(tmpDir, "reports").createDirectoryWhenNoExist
      val userId = Tool.getUserId
      data.ids.foreach {
        id =>
          val sampleIdDir = Tool.getSampleIdDir(userId, id)
          new File(sampleIdDir, s"${
            id
          }.xlsx").fileCopyToDir(reportDir)
      }
      val zipFile = new File(tmpDir, "reports.zip")
      ZipUtil.pack(reportDir, zipFile)
      Ok.sendFile(zipFile, onClose = () => {
        Tool.deleteDirectory(tmpDir)
      })
  }

  def sampleCompareBefore = Action { implicit request =>
    val missionName = s"compare_${Tool.generateMissionName}"
    Ok(views.html.user.sampleCompare(missionName))
  }

  def newCompareMission = Action.async(parse.multipartFormData) { implicit request =>
    val data = FormTool.compareMissionForm.bindFromRequest().get
    val userId = Tool.getUserId
    val row = MissionRow(0, s"${data.missionName}", "", userId, VarTool.compareMissionStr, new DateTime(), None, "preparing")
    missionDao.insert(row).flatMap(_ => missionDao.select(row.userId, row.missionName, VarTool.compareMissionStr)).flatMap { mission =>
      val outDir = Tool.getUserCompareMissionDir
      val missionDir = MissionUtils.getMissionDir(mission.id, outDir)
      val (workspaceDir, resultDir) = (missionDir.workspaceDir, missionDir.resultDir)
      val configFile = Tool.produceConfigFile(data, workspaceDir)
      val newMission = mission.copy(state = "wait")
      missionDao.update(newMission).map { x =>
        Ok(Json.obj("valid" -> true))
      }
    }
  }

  def missionNameCheck = Action.async { implicit request =>
    val data = FormTool.missionNameCheckForm.bindFromRequest.get
    val userId = Tool.getUserId
    missionDao.selectOption(userId, data.missionName, data.kind).map { mission =>
      mission match {
        case Some(y) => Ok(Json.obj("valid" -> false))
        case None =>
          Ok(Json.obj("valid" -> true))
      }
    }
  }

  def compareMissionDetailBefore = Action { implicit request =>
    val data = FormTool.idForm.bindFromRequest().get
    Ok(views.html.user.compareMissionDetail(data.id))
  }

  def getMissionInfoById = Action.async { implicit request =>
    val data = FormTool.missionIdForm.bindFromRequest().get
    val userId = Tool.getUserId
    missionDao.select(data.missionId).map { mission =>
      val workspaceDir = Tool.getUserCompareMissionWorkspaceDir(mission.id)
      val configFile = Tool.getConfigFile(workspaceDir)
      val configJson = configFile.str.toJson.toJsObject
      val missionJson = Utils.getJsonByT(mission).toJsObject
      Ok((configJson ++ missionJson))
    }
  }

  def getCompareReadsData = Action.async { implicit request =>
    val data = FormTool.missionIdForm.bindFromRequest().get
    val userId = Tool.getUserId
    missionDao.select(data.missionId).map { mission =>
      val workspaceDir = Tool.getUserCompareMissionWorkspaceDir(mission.id)
      val configFile = Tool.getConfigFile(workspaceDir)
      val configJson = configFile.str.toJson
      val configData = FormTool.compareMissionForm.bind(configJson).get
      val jsons = List(configData.sample1, configData.sample2).map { id =>
        val sampleIdDir = SampleTool.getSampleIdDir(userId, id)
        val readsFile = Tool.getReadsFile(sampleIdDir)
        val json = Utils.getTxtFileJsonNoLower(readsFile)
        Json.obj("id" -> id, "data" -> json)
      }
      Ok(Json.toJson(jsons))
    }
  }

  def getCompareRawSeqData = Action.async { implicit request =>
    val data = FormTool.missionIdForm.bindFromRequest().get
    val userId = Tool.getUserId
    missionDao.select(data.missionId).map { mission =>
      val workspaceDir = Tool.getUserCompareMissionWorkspaceDir(mission.id)
      val configFile = Tool.getConfigFile(workspaceDir)
      val configJson = configFile.str.toJson
      val configData = FormTool.compareMissionForm.bind(configJson).get
      val maps = List(configData.sample1, configData.sample2)
      Ok(Json.toJson(maps))
    }
  }

  def getCompareRawSnpSeqData = Action.async { implicit request =>
    val data = FormTool.missionIdForm.bindFromRequest().get
    val userId = Tool.getUserId
    missionDao.select(data.missionId).map { mission =>
      val workspaceDir = Tool.getUserCompareMissionWorkspaceDir(mission.id)
      val configFile = Tool.getConfigFile(workspaceDir)
      val configJson = configFile.str.toJson
      val configData = FormTool.compareMissionForm.bind(configJson).get
      val maps = List(configData.sample1, configData.sample2)
      Ok(Json.toJson(maps))
    }
  }

  def getCompareDiffReadsData = Action.async { implicit request =>
    val data = FormTool.missionIdForm.bindFromRequest().get
    val userId = Tool.getUserId
    missionDao.select(data.missionId).map { mission =>
      val workspaceDir = Tool.getUserCompareMissionWorkspaceDir(mission.id)
      val diffReadsDataFile = Tool.getDiffReadsDataFile(workspaceDir)
      val json = diffReadsDataFile.str.toJson
      Ok(json)
    }
  }

  def getCompareSnpReadsData = Action.async { implicit request =>
    val data = FormTool.missionIdForm.bindFromRequest().get
    val userId = Tool.getUserId
    missionDao.select(data.missionId).map {
      mission =>
        val workspaceDir = Tool.getUserCompareMissionWorkspaceDir(mission.id)
        val configFile = Tool.getConfigFile(workspaceDir)
        val configJson = configFile.str.toJson
        val configData = FormTool.compareMissionForm.bind(configJson).get
        val jsons = List(configData.sample1, configData.sample2).map {
          id =>
            val sampleIdDir = SampleTool.getSampleIdDir(userId, id)
            val readsFile = Tool.getSnpReadsFile(sampleIdDir)
            val json = Utils.getTxtFileJsonNoLower(readsFile)
            Json.obj("id" -> id, "data" -> json)
        }
        Ok(Json.toJson(jsons))
    }
  }

  def getCompareVennData = Action.async {
    implicit request =>
      val data = FormTool.missionIdForm.bindFromRequest().get
      val userId = Tool.getUserId
      missionDao.select(data.missionId).map {
        mission =>
          val workspaceDir = Tool.getUserCompareMissionWorkspaceDir(mission.id)
          val vennDataFile = Tool.getVennDataFile(workspaceDir)
          val json = vennDataFile.str.toJson
          Ok(json)
      }
  }

  def getCompareDiffData = Action.async {
    implicit request =>
      val data = FormTool.missionIdForm.bindFromRequest().get
      val userId = Tool.getUserId
      missionDao.select(data.missionId).map {
        mission =>
          val workspaceDir = Tool.getUserCompareMissionWorkspaceDir(mission.id)
          val configFile = Tool.getConfigFile(workspaceDir)
          val configJson = configFile.str.toJson
          val configData = FormTool.compareMissionForm.bind(configJson).get
          val readsDatas = List(configData.sample1, configData.sample2).map {
            id =>
              val sampleIdDir = SampleTool.getSampleIdDir(userId, id)
              val snpReadsFile = Tool.getSnpReadsFile(sampleIdDir)
              val snpReads = snpReadsFile.txtLines.drop(1).map {
                columns =>
                  (columns(0), columns(1))
              }.groupBy(_._1).mapValues(x => x.map(y => y._2).toSet).map {
                case (locus, genotypes) =>
                  val t = (locus, "snp")
                  (t, genotypes)
              }.toMap
              val readsFile = Tool.getReadsFile(sampleIdDir)
              val reads = readsFile.txtLines.drop(1).map {
                columns =>
                  (columns(0), columns(1))
              }.groupBy(_._1).mapValues(x => x.map(y => y._2).toSet).map {
                case (locus, genotypes) =>
                  val t = (locus, "str")
                  (t, genotypes)
              }.toMap
              (id, (snpReads ++ reads))
          }
          val interNum = readsDatas(0)._2.filter {
            case (t, genotypes) =>
              val otherData = readsDatas(1)._2
              val otherGenotypes = otherData(t)
              otherGenotypes == genotypes
          }.size
          val eachReads = readsDatas.map {
            x =>
              Json.obj("id" -> x._1, "num" -> x._2.size)
          }
          Ok(Json.obj("each" -> eachReads, "inter" -> interNum))
      }
  }

  def getCompareDiffSnpReadsData = Action.async {
    implicit request =>
      val data = FormTool.missionIdForm.bindFromRequest().get
      val userId = Tool.getUserId
      missionDao.select(data.missionId).map { mission =>
        val workspaceDir = Tool.getUserCompareMissionWorkspaceDir(mission.id)
        val diffSnpFile = Tool.getDiffSnpDataFile(workspaceDir)
        val json = diffSnpFile.str.toJson
        Ok(Json.toJson(json))
      }
  }

  def refreshSelectColumns = Action.async {
    implicit request =>
      val data = FormTool.columnNamesForm.bindFromRequest().get
      val userId = Tool.getUserId
      val row = UserColumnRow(userId, Json.toJson(data.columnNames).toString())
      userColumnDao.insertOrUpdate(row).map { mission =>
        Ok(Json.toJson("success!"))
      }
  }

  def getSelectColumns = Action.async {
    implicit request =>
      val userId = Tool.getUserId
      userColumnDao.selectOption(userId).map { userColumnsOp =>
        val defaultShowColumns = (VarTool.basicColumnNames ::: VarTool.allSiteNames).filterNot { x =>
          List("updateMission", "indexId", "kind", "comment", "interlocusBalance").contains(x)
        }
        val json = userColumnsOp.map { userColumn =>
          Json.parse(userColumn.selectColumns)
        }.getOrElse(Json.toJson(defaultShowColumns))
        Ok(json)
      }
  }

  def exportDta = Action.async { implicit request =>
    val data = FormTool.idsForm.bindFromRequest().get
    val kitName = FormTool.kitNameForm.bindFromRequest().get.kitName
    val exportDatData = FormTool.exportDatForm.bindFromRequest().get
    val tmpDir = Tool.createTempDirectory("tmpDir")
    val outFile =new File(tmpDir, "out.file")
    val userId = Tool.getUserId
    sampleDao.selectAll(userId, data.ids).map { samples =>
      Tool.samples2Dat(tmpDir,samples.toList,kitName,exportDatData)
      Ok.sendFile(outFile, onClose = () => {
        Tool.deleteDirectory(tmpDir)
      }).withHeaders(
        CONTENT_DISPOSITION -> s"attachment; filename=$outFile",
        "HttpResponse.entity.contentType" -> "application/x-download"
      )
    }
  }

  def exportAllDat = Action.async { implicit request =>
    val kitName = FormTool.kitNameForm.bindFromRequest().get.kitName
    val data = FormTool.pageQueryForm.bindFromRequest().get
    val exportDatData = FormTool.exportDatForm.bindFromRequest().get
    val tmpDir = Tool.createTempDirectory("tmpDir")
    val outFile =new File(tmpDir, "out.file")
    val userId = Tool.getUserId
    sampleDao.selectAllNotSort(userId, data).map { samples =>
      Tool.samples2Dat(tmpDir,samples.toList,kitName,exportDatData)
      Ok.sendFile(outFile, onClose = () => {
        Tool.deleteDirectory(tmpDir)
      })
    }
  }

}
